<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="SlnEA" id="SlnEA"><span data-lake-id="u1a337a6f" id="u1a337a6f">典型回答</span></h1>
  <p data-lake-id="ufa1cde17" id="ufa1cde17"><br></p>
  <p data-lake-id="u7c923351" id="u7c923351"><span data-lake-id="u1338542d" id="u1338542d">首先，在非并发的场景中，出现不一致的问题大家都能比较容易的理解，因为缓存的操作和数据库的操作是存在一定的时间差的。而且这两个操作是没办法保证原子性的，也就是说，是有可能一个操作成功，一个操作失败的。所以，这就必然会存在不一致的情况。</span></p>
  <p data-lake-id="uaef3325e" id="uaef3325e"><span data-lake-id="u77aa1523" id="u77aa1523">​</span><br></p>
  <p data-lake-id="u2dc2b85e" id="u2dc2b85e"><span data-lake-id="u72fb7fa1" id="u72fb7fa1">如果在并发场景中，如果两个线程，同时进行先写数据库，后更新缓存的操作，就可能会出现不一致：</span></p>
  <p data-lake-id="uce6ab093" id="uce6ab093"><span data-lake-id="u9ef63407" id="u9ef63407">​</span><br></p>
  <table data-lake-id="EZLVD" id="EZLVD" margin="true" class="lake-table" style="width: 750px">
   <colgroup>
    <col width="375">
    <col width="375">
   </colgroup>
   <tbody>
    <tr data-lake-id="u6859495e" id="u6859495e">
     <td data-lake-id="u07a9553d" id="u07a9553d" style="background-color: rgb(249, 249, 249)">
      <p data-lake-id="u0f047992" id="u0f047992" style="text-align: center"><span data-lake-id="uf07dd1b4" id="uf07dd1b4" class="lake-fontsize-9" style="color: rgb(85, 85, 85)">W</span></p></td>
     <td data-lake-id="ub9cddbdc" id="ub9cddbdc" style="background-color: rgb(249, 249, 249)">
      <p data-lake-id="u7ffea0d0" id="u7ffea0d0" style="text-align: center"><span data-lake-id="ue278d135" id="ue278d135" class="lake-fontsize-9" style="color: rgb(85, 85, 85)">W</span></p></td>
    </tr>
    <tr data-lake-id="ua1a7b543" id="ua1a7b543">
     <td data-lake-id="u7ae82e2c" id="u7ae82e2c">
      <p data-lake-id="u37a7e474" id="u37a7e474" style="text-align: justify"><span data-lake-id="u0e52a571" id="u0e52a571" class="lake-fontsize-9" style="color: rgb(85, 85, 85)">写数据库，更新成20</span></p></td>
     <td data-lake-id="u20b2678b" id="u20b2678b"></td>
    </tr>
    <tr data-lake-id="ua0b9bd13" id="ua0b9bd13">
     <td data-lake-id="u9b5eb99a" id="u9b5eb99a"></td>
     <td data-lake-id="ucc629572" id="ucc629572">
      <p data-lake-id="ub5825c12" id="ub5825c12" style="text-align: justify"><span data-lake-id="u25397b4b" id="u25397b4b" class="lake-fontsize-9" style="color: rgb(85, 85, 85)">写数据库，更新成10</span></p></td>
    </tr>
    <tr data-lake-id="u614b5a8a" id="u614b5a8a">
     <td data-lake-id="u3ac97c73" id="u3ac97c73"></td>
     <td data-lake-id="u2182c744" id="u2182c744">
      <p data-lake-id="u1a40eaa4" id="u1a40eaa4" style="text-align: justify"><span data-lake-id="ubb618e9f" id="ubb618e9f" class="lake-fontsize-9" style="color: rgb(85, 85, 85)">写缓存，更新成10</span></p></td>
    </tr>
    <tr data-lake-id="u751ff110" id="u751ff110">
     <td data-lake-id="u8c11755d" id="u8c11755d">
      <p data-lake-id="u1130686e" id="u1130686e" style="text-align: justify"><strong><span data-lake-id="u542a4413" id="u542a4413" class="lake-fontsize-9" style="color: rgb(85, 85, 85)">写缓存，更新成20（数据不一致）</span></strong></p></td>
     <td data-lake-id="u7010fa9b" id="u7010fa9b"></td>
    </tr>
   </tbody>
  </table>
  <p data-lake-id="u3f5e50aa" id="u3f5e50aa" style="text-align: justify"><span data-lake-id="uc68c8f8b" id="uc68c8f8b" class="lake-fontsize-12" style="color: rgb(85, 85, 85)">​</span><br></p>
  <p data-lake-id="uaa8cb6bf" id="uaa8cb6bf" style="text-align: justify"><span data-lake-id="uc80334ce" id="uc80334ce">如果在并发场景中，如果两个线程，同时进行先更新缓存，后写数据库的操作，同理，也可能会出现不一致：</span></p>
  <p data-lake-id="u37295dae" id="u37295dae" style="text-align: justify"><span data-lake-id="ue284104f" id="ue284104f" class="lake-fontsize-12" style="color: rgb(85, 85, 85)">​</span><br></p>
  <table data-lake-id="CCLtA" id="CCLtA" margin="true" class="lake-table" style="width: 750px">
   <colgroup>
    <col width="375">
    <col width="375">
   </colgroup>
   <tbody>
    <tr data-lake-id="uaf318477" id="uaf318477">
     <td data-lake-id="ucf21c840" id="ucf21c840" style="background-color: rgb(249, 249, 249)">
      <p data-lake-id="u0dbf92e8" id="u0dbf92e8" style="text-align: center"><span data-lake-id="ufef8e5a2" id="ufef8e5a2" class="lake-fontsize-9" style="color: rgb(85, 85, 85)">W</span></p></td>
     <td data-lake-id="u9bab3c62" id="u9bab3c62" style="background-color: rgb(249, 249, 249)">
      <p data-lake-id="u2cd97fae" id="u2cd97fae" style="text-align: center"><span data-lake-id="udb985d63" id="udb985d63" class="lake-fontsize-9" style="color: rgb(85, 85, 85)">W</span></p></td>
    </tr>
    <tr data-lake-id="u764777cf" id="u764777cf">
     <td data-lake-id="u1104cc64" id="u1104cc64">
      <p data-lake-id="ue3bfe274" id="ue3bfe274" style="text-align: justify"><span data-lake-id="u0733c5a7" id="u0733c5a7" class="lake-fontsize-9" style="color: rgb(85, 85, 85)">写缓存，更新成20</span></p></td>
     <td data-lake-id="u7025592d" id="u7025592d"></td>
    </tr>
    <tr data-lake-id="u1395091b" id="u1395091b">
     <td data-lake-id="ua753b34b" id="ua753b34b"></td>
     <td data-lake-id="u2e8950c8" id="u2e8950c8">
      <p data-lake-id="ua7fe70a4" id="ua7fe70a4" style="text-align: justify"><span data-lake-id="u4e5654ba" id="u4e5654ba" class="lake-fontsize-9" style="color: rgb(85, 85, 85)">写缓存，更新成10</span></p></td>
    </tr>
    <tr data-lake-id="udf8fbbc7" id="udf8fbbc7">
     <td data-lake-id="ub7974afa" id="ub7974afa"></td>
     <td data-lake-id="u7b401adb" id="u7b401adb">
      <p data-lake-id="u272edd44" id="u272edd44" style="text-align: justify"><span data-lake-id="ua3a3921e" id="ua3a3921e" class="lake-fontsize-9" style="color: rgb(85, 85, 85)">写数据库，更新成10</span></p></td>
    </tr>
    <tr data-lake-id="u9bad2ec0" id="u9bad2ec0">
     <td data-lake-id="u7d499364" id="u7d499364">
      <p data-lake-id="u8b256909" id="u8b256909" style="text-align: justify"><strong><span data-lake-id="u3cd4d189" id="u3cd4d189" class="lake-fontsize-9" style="color: rgb(85, 85, 85)">写数据库，更新成20（数据不一致）</span></strong></p></td>
     <td data-lake-id="u8925c5ad" id="u8925c5ad"></td>
    </tr>
   </tbody>
  </table>
  <p data-lake-id="u4c63afd7" id="u4c63afd7"><span data-lake-id="u674a09a5" id="u674a09a5">​</span><br></p>
  <p data-lake-id="u6771c732" id="u6771c732"><span data-lake-id="u523e2e13" id="u523e2e13">​</span><br></p>
  <p data-lake-id="uda636eb4" id="uda636eb4"><span data-lake-id="u4bcb2b3f" id="u4bcb2b3f">在并发场景中，还有一种容易忽略的并发场景，那就是读写并发。</span></p>
  <p data-lake-id="u15fd4314" id="u15fd4314"><span data-lake-id="ua0150228" id="ua0150228">​</span><br></p>
  <p data-lake-id="uc3e78f73" id="uc3e78f73"><span data-lake-id="u5b9919ac" id="u5b9919ac">我们知道，当我们使用了缓存之后，一个读的线程在查询数据的过程是这样的：</span></p>
  <p data-lake-id="uace598a7" id="uace598a7"><span data-lake-id="ua523f715" id="ua523f715">​</span><br></p>
  <ul list="ud12de7a7">
   <li fid="udd20c07f" data-lake-id="u5a267623" id="u5a267623"><span data-lake-id="ubf328c46" id="ubf328c46">1、查询缓存，如果缓存中有值，则直接返回 </span></li>
   <li fid="udd20c07f" data-lake-id="uc7419334" id="uc7419334"><span data-lake-id="ue9265aa7" id="ue9265aa7">2、查询数据库 </span></li>
   <li fid="udd20c07f" data-lake-id="u06e43135" id="u06e43135"><span data-lake-id="u8625c584" id="u8625c584">3、把数据库的查询结果更新到缓存中</span></li>
  </ul>
  <p data-lake-id="u34ec816b" id="u34ec816b"><span data-lake-id="u7fe90959" id="u7fe90959">​</span><br></p>
  <p data-lake-id="udaae14d5" id="udaae14d5"><span data-lake-id="uf2828e18" id="uf2828e18">所以，</span><span data-lake-id="u4022500a" id="u4022500a">对于一个读线程来说，虽然不会写数据库，但是是会更新缓存的</span><span data-lake-id="u949e5274" id="u949e5274">，所以，在一些特殊的并发场景中，就会导致数据不一致的情况。</span></p>
  <p data-lake-id="u3c046bc5" id="u3c046bc5"><span data-lake-id="u0fcd5d03" id="u0fcd5d03">​</span><br></p>
  <p data-lake-id="u58da4a88" id="u58da4a88"><span data-lake-id="u17fb45b0" id="u17fb45b0">读写并发的时序如下：</span></p>
  <table data-lake-id="dVNbw" id="dVNbw" margin="true" class="lake-table" style="width: 750px">
   <colgroup>
    <col width="375">
    <col width="375">
   </colgroup>
   <tbody>
    <tr data-lake-id="u337d39b1" id="u337d39b1">
     <td data-lake-id="u5c8fcb00" id="u5c8fcb00" style="background-color: rgb(249, 249, 249)">
      <p data-lake-id="u48c9b003" id="u48c9b003" style="text-align: center"><span data-lake-id="ueb3bbbc2" id="ueb3bbbc2" class="lake-fontsize-9" style="color: rgb(85, 85, 85)">W</span></p></td>
     <td data-lake-id="u15192feb" id="u15192feb" style="background-color: rgb(249, 249, 249)">
      <p data-lake-id="u60a16467" id="u60a16467" style="text-align: center"><span data-lake-id="u7bc805e8" id="u7bc805e8" class="lake-fontsize-9" style="color: rgb(85, 85, 85)">R</span></p></td>
    </tr>
    <tr data-lake-id="u2d751722" id="u2d751722">
     <td data-lake-id="uce21a786" id="uce21a786"></td>
     <td data-lake-id="u48b2164e" id="u48b2164e">
      <p data-lake-id="u016ec355" id="u016ec355" style="text-align: justify"><span data-lake-id="uda756b2c" id="uda756b2c" class="lake-fontsize-9" style="color: rgb(85, 85, 85)">读缓存，缓存中没有值</span></p></td>
    </tr>
    <tr data-lake-id="ucb137bba" id="ucb137bba">
     <td data-lake-id="u5d5b3624" id="u5d5b3624"></td>
     <td data-lake-id="u99c2a3b7" id="u99c2a3b7">
      <p data-lake-id="u7e09ee2d" id="u7e09ee2d" style="text-align: justify"><span data-lake-id="u8c20ed09" id="u8c20ed09" class="lake-fontsize-9" style="color: rgb(85, 85, 85)">读数据库，数据库中得到结果为10</span></p></td>
    </tr>
    <tr data-lake-id="u9c259c45" id="u9c259c45">
     <td data-lake-id="u42fc3598" id="u42fc3598">
      <p data-lake-id="ufb5bf3f5" id="ufb5bf3f5" style="text-align: justify"><span data-lake-id="u2678e1ff" id="u2678e1ff" class="lake-fontsize-9" style="color: rgb(85, 85, 85)">写数据库和缓存，更新成20</span></p></td>
     <td data-lake-id="u41f82edf" id="u41f82edf"></td>
    </tr>
    <tr data-lake-id="ua5a19784" id="ua5a19784">
     <td data-lake-id="ub3a0ea72" id="ub3a0ea72"></td>
     <td data-lake-id="uaee75c76" id="uaee75c76">
      <p data-lake-id="ue0c7a3cb" id="ue0c7a3cb" style="text-align: justify"><strong><span data-lake-id="ub8a958e4" id="ub8a958e4" class="lake-fontsize-9" style="color: rgb(85, 85, 85)">写缓存，更新成10（数据不一致）</span></strong></p></td>
    </tr>
   </tbody>
  </table>
  <p data-lake-id="uba746caf" id="uba746caf"><span data-lake-id="u078f00d2" id="u078f00d2">也就是说，假如一个读线程，在读缓存的时候没查到值，他就会去数据库中查询，但是如果自查询到结果之后，更新缓存之前，数据库被更新了，但是这个读线程是完全不知道的，那么就导致最终缓存会被重新用一个”旧值”覆盖掉。</span></p>
  <p data-lake-id="u81fcef73" id="u81fcef73"><span data-lake-id="u4d4ea321" id="u4d4ea321">​</span><br></p>
  <p data-lake-id="u8735a1ae" id="u8735a1ae"><span data-lake-id="u00fd3e76" id="u00fd3e76">这也就导致了</span><span data-lake-id="u3e948e82" id="u3e948e82">缓存和数据库的不一致的现象</span><span data-lake-id="uc51c80d1" id="uc51c80d1">。</span></p>
  <p data-lake-id="u9915c2db" id="u9915c2db"><span data-lake-id="ued6ec5b2" id="ued6ec5b2">​</span><br></p>
  <p data-lake-id="ue0818a36" id="ue0818a36"><span data-lake-id="u4c5461cc" id="u4c5461cc">但是这种现象其实发生的</span><span data-lake-id="ua8e0d26f" id="ua8e0d26f">概率比较低</span><span data-lake-id="u6b73b486" id="u6b73b486">，因为一般一个读操作是很快的，数据库+缓存的读操作基本在十几毫秒左右就可以完成了。</span></p>
  <p data-lake-id="u57fb451c" id="u57fb451c"><span data-lake-id="u2145f593" id="u2145f593">​</span><br></p>
  <p data-lake-id="ud1ea3080" id="ud1ea3080"><span data-lake-id="ue7af7dcb" id="ue7af7dcb">而在这期间，刚好另一个线程执行了一个比较耗时的写操作的概率确实比较低。</span></p>
  <p data-lake-id="u3233af53" id="u3233af53"><span data-lake-id="u5ca3de1f" id="u5ca3de1f">当然，根据墨菲定律，只要有可能发生的事情，就一定会发生。所以我们也要引起重视。</span></p>
  <p data-lake-id="u5f2fb970" id="u5f2fb970"><span data-lake-id="uc424cd06" id="uc424cd06">​</span><br></p>
 </body>
</html>